//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================

#include <winhack.h>
#include <elasys_server.h>
#include <string.h>
#include <elapi.h>
#include <stdlib.h>
#include <netcard.h>
#include <stdio.h>
#include <msgqueue.h>
#include <sysconfig.h>
#include <driver.h>

#include "ethermain.h"

#define ETHIPHEAD_LENGTH          0x22
#define PROTOCOL_OFFSET           0x17
#define TCP_PROTOCOL              0x06
#define UDP_PROTOCOL              0x11
#define SRCADDR_OFFSET            0x1a
#define DSTADDR_OFFSET            0x1e
#define IP_PROTOCOL               0x08
#define TCP_CONN_HEAD_LEN         0x34
#define UDP_CONN_HEAD_LEN         0x2a
#define TCP_CONN_CHECKSUM_OFFSET  0x32
#define UDP_CONN_CHECKSUM_OFFSET  0x28
#define IP_HEAD_LEN_OFFSET        0xe
#define TCP_HEAD_LEN_OFFSET       0x2e
#define TOTAL_LEN_OFFSET_HIGH     0x11
#define TOTAL_LEN_OFFSET_LOW      0x10
#define IP_PROTOCOL_GENRE         0xc

#define _htons(n)(((n&0xFF)<<8) | ((n>>8)&0xFF))
// MAC address declared in etherimp.cpp
extern unsigned char    cMacAddr[6];
extern wchar_t          wEtherID[100];

EXTERN_C BOOL EtherInit();
EXTERN_C void EtherSendPacket(char *buf, unsigned io_len);
EXTERN_C void EtherReceivePacket();

extern Node g_nodereadlist;


typedef struct tsd_hdr
{
    UInt32   srcaddr;
    UInt32   dstaddr;
    Int8     mbz;
    Int8     ptcl;
    UInt16   tcpl;
}PSD_HEADER;

UInt16 CheckSum(UInt16 *buffer, Int32 size)
{
    UInt32 chksum = 0;
    while(size >1)
    {
        chksum += *buffer++;
        size -= sizeof(WORD);
    }
    if(size)
        chksum += *(UInt8*)buffer;
    chksum = (chksum >> 16) + (chksum & 0xffff);
    chksum += (chksum >> 16);
    return (WORD)(~chksum);
}

void ModifyCheckSum(UInt8* data, Int32 len)
{
    UInt8 tempBuf[1600];

    if (len < ETHIPHEAD_LENGTH || len >= (Int32)sizeof(tempBuf))
        return ;

    UInt8* checkData = data + ETHIPHEAD_LENGTH;
    Int32 checkLen = len - ETHIPHEAD_LENGTH;
    UInt8 protocol = *(data + PROTOCOL_OFFSET);
    UInt16 ipProtocol = *((Int16*)(data + IP_PROTOCOL_GENRE));
    if (ipProtocol != IP_PROTOCOL)
        return ;

    if(TCP_PROTOCOL != protocol && UDP_PROTOCOL != protocol)
        return;

    PSD_HEADER  PsdHeader;
    PsdHeader.srcaddr = *((UInt32*)(data + SRCADDR_OFFSET));
    PsdHeader.dstaddr = *((UInt32*)(data + DSTADDR_OFFSET));
    PsdHeader.mbz = 0;
    PsdHeader.ptcl = protocol;

    if (TCP_PROTOCOL == protocol) {
        if (len < TCP_CONN_HEAD_LEN)
            return ;
        Int32 ipheaderLen = (*(data+IP_HEAD_LEN_OFFSET)&0x0f) << 2;
        Int32 tcpheaderLen = ((*(data+TCP_HEAD_LEN_OFFSET)>>4)&0x0f) << 2;
        UInt16 totalLen = data[TOTAL_LEN_OFFSET_HIGH] + (data[TOTAL_LEN_OFFSET_LOW]<<8);
        UInt32 dataSize = (totalLen - tcpheaderLen - ipheaderLen);
        Int32 shouldCopy = (dataSize != 0) ? checkLen : tcpheaderLen;
        PsdHeader.tcpl = _htons(shouldCopy);
        memcpy(tempBuf, &PsdHeader, sizeof(PSD_HEADER) );
        memcpy(tempBuf + sizeof(PSD_HEADER), checkData, shouldCopy);
        UInt16* checkSum = (UInt16*)(data + TCP_CONN_CHECKSUM_OFFSET);
        *((UInt16*)(tempBuf + sizeof(PsdHeader) + TOTAL_LEN_OFFSET_LOW)) = 0;
        *checkSum = CheckSum( (UInt16 *)tempBuf, sizeof(PsdHeader) + shouldCopy);
    }
    else if(UDP_PROTOCOL == protocol)
    {
        if (len < UDP_CONN_HEAD_LEN)
            return ;
        PsdHeader.tcpl = _htons(checkLen);
        memcpy(tempBuf, &PsdHeader, sizeof(PSD_HEADER));
        memcpy(tempBuf + sizeof(PSD_HEADER),checkData, checkLen);
        UInt16* checkSum = (UInt16*)(data + UDP_CONN_CHECKSUM_OFFSET);
        *((UInt16*)(tempBuf + sizeof(PsdHeader) + 6)) = 0;
        *checkSum = CheckSum((UInt16*)tempBuf, sizeof(PsdHeader) + checkLen);
    }
}

ECode NetCard::Read(
    /* [in] */ Int64 u64Offset,
    /* [in] */ Int32 bytesToRead,
    /* [out] */ MemoryBuf * pBuffer,
    /* [out] */ IEvent * * ppCompletionEvent)
{
   // printf("+NetCard::Read\n");

    // Not empty, return, else read until not empty
    while (g_nodereadlist.IsEmpty()) {
        EtherReceivePacket();
    }

    Node * pnode = g_nodereadlist.GetNode();

    if (pBuffer == NULL) {
        return E_INVALID_ARGUMENT;
    }
    ModifyCheckSum((UInt8 *)pnode->m_address, pnode->m_len);
    pBuffer->Copy((Byte *)pnode->m_address, pnode->m_len);
    delete pnode;

    //printf("-NetCard::Read\n");
    return NOERROR;
}

ECode NetCard::Write(
    /* [in] */ Int64 u64Offset,
    /* [in] */ const MemoryBuf & buffer,
    /* [out] */ Int32 * pBytesWritten,
    /* [out] */ IEvent * * ppCompletionEvent)
{
    //printf("+NetCard::Write. size: %d\n", buffer.GetUsed());

    char * pBuf = (char *)buffer.GetPayload();

    // send the packet
    EtherSendPacket(pBuf, buffer.GetUsed());
    //printf("-NetCard::Write\n");
    return NOERROR;
}

ECode NetCard::Control(
    /* [in] */ Handle32 nControlCode,
    /* [in] */ const MemoryBuf & inBuffer,
    /* [out] */ MemoryBuf * pOutBuffer,
    /* [out] */ IEvent ** ppCompletionEvent)
{
    // printf("NetCard::Control,  code is %d\n", nControlCode);

    if (nControlCode == DRVINITALIZE) {
        // Init virtual network driver
        if (!EtherInit()) {
            wprintf(L"NetCard::Control: At file %d, line %d.\n", __FILE__, __LINE__);
            return E_INVALID_OPERATION;
        }

        printf("Return Mac: %02x:%02x:%02x:%02x:%02x:%02x\n",
            cMacAddr[0],cMacAddr[1],cMacAddr[2],
            cMacAddr[3],cMacAddr[4],cMacAddr[5]);

        // return the MAC address
        if (pOutBuffer == NULL) {
            return E_INVALID_ARGUMENT;
        }
        pOutBuffer->Copy(cMacAddr, 6);
    }
    else if (nControlCode == DRVSHUTDOWN) {
        ;
    }
    else {
        printf("unknown ControlCode %d\n", nControlCode);
    }
    return NOERROR;
}

void NetCard::Dispose()
{
    //delete this;
}

IDeviceDriver *pDriver;//use for wrap_sharp.cpp

EXTERN IDeviceDriver * CDECL CreateNetCard(uint_t uDeviceNo, void *pvParameter)
{
    pDriver = new NetCard();
    if (NULL == pDriver) {
        return NULL;
    }
    pDriver->AddRef();

    // Get Ethernet Card ID and Mac Address
    WStringBuf wEtherIDBuf(wEtherID, sizeof(wEtherID) / sizeof(wchar_t));
    //ECode ec = _CProfile_GetWString(L"Tcpip", L"EtherCardID", (WChar *)wEtherID, 100);
    ECode ec = _CProfile_GetWString(L"Tcpip", L"EtherCardID", &wEtherIDBuf);
    if (FAILED(ec)) {
        printf("Failed to Read Ethernet Configuration.\n");
        delete pDriver;
        return NULL;
    }

    wprintf(L"Use Ethernet Card %s\n", wEtherID);

    // Generate Mac Address randomly.
    SystemTime tm;
    _CSystem_GetMachineTime(&tm);

    srand(tm.seconds + tm.microseconds);

    int i = 0;
    cMacAddr[0] = 0x00;
    cMacAddr[1] = 0x0E;
    cMacAddr[2] = 0xE8;
    for (i = 3; i < 6; i++) {
        cMacAddr[i] = rand() % 255;
    }

    return pDriver;
}

